﻿using System;
using System.Collections.Generic;
using llAma.Base;
using System.Text;
using llAma.Interface;
using MySql.Data.MySqlClient;
using llAma.Backend.Interface;
using llAma;
using dAmn;
using llamaServer;
using System.Text.RegularExpressions;

namespace socialdeveloperBackend
{
    public class ChatChannel : ChannelBase
    {
        private int mId = -1;
        public int ID
        {
            get
            {
                return mId;
            }
        }
        public static bool ShouldExpire(ChatChannel c)
        {
            if (c != null)
                return c.CanExpire;
            else
                return true;
        }
        public bool CanExpire
        {
            get { return this.GetAllConnectionsPresent().Length == 0 ? true : false; }
        }
        private int mDpc = -1;
        public int defaultPC
        {
            get
            {
                return mDpc;
            }
        }
        public static ChatChannel GetChatChannel(string full_namespace)
        {
            ChatChannel ret = null;
            string[] parts = full_namespace.Split(':');
            if (parts.Length != 2)
                return null;
            using (MySqlConnection conn = SDBackend.CreateConnection())
            {
                using (MySqlDataReader dr = SDBackend.ExecuteReader(conn, "SELECT * FROM tbl_namespaces WHERE toplevel = ?ns AND name = ?channel",
                    new MySqlParameter("?ns", parts[0]), new MySqlParameter("?channel", parts[1])))
                {
                    if (dr.Read())
                    {
                        ret = new ChatChannel();
                        //This will correct the namespace, and return a properly named channel. FIXES ISSUE: Channle names are cased by the first user to join
                        //Instead of Cased by how they are stored in DB (Created)
                        ret.Namespace = string.Join(":", new string[] { (string)dr["toplevel"], (string)dr["name"] });
                        ret.mId = (int)dr["id"];
                        ret.mDpc = (int)dr["defaultpc"];

                    }
                    else
                        return null;
                }
            }
            return ret;
        }
        internal ChatChannel()
            : base()
        {
        }
        protected override string GetPrivclassName(IConnection conn)
        {
            return ((DBPropertyManager)PropertyManager).GetPrivclassName(conn);
        }
        protected override bool CanUserJoin(llAma.Backend.Interface.IConnection sender)
        {
            return true;
        }
        public override bool Demand(IConnection conn, PermissionRule r)
        {
            return ((DBPropertyManager)PropertyManager).Demand(conn, r);
        }
        DBPropertyManager mMyManager = null;

        protected override IPropertyManager PropertyManager
        {
            get
            {
                if (mMyManager == null)
                    mMyManager = new DBPropertyManager(this);
                return mMyManager;

            }
        }

        internal void ForceReload()
        {
            //Reload the property manager.
            mMyManager = new DBPropertyManager(this);
            string[] packets = new string[] { "title", "topic", "motd", "botdirective", "privclasses", "members" };
            //Blast out the property packets on reload, We don't know what changed.
            IConnection[] conns = GetAllConnectionsPresent();
            foreach (IConnection c in conns)
            {
                for (int i = 0; i < packets.Length; i++)
                {
                    Packet p = Packet.parse(string.Format("get {0}\np={1}\n\n", Namespace, packets[i]));
                    ((IPropertyManager)mMyManager).HandlePacket(p, c);
                }

            }

        }
        protected override bool CheckKick(IConnection sender, IConnection target)
        {
            PrivclassInfo SPI = mMyManager.GetPrivclassFromName(mMyManager.GetPrivclassName(sender));
            PrivclassInfo TPI = mMyManager.GetPrivclassFromName(mMyManager.GetPrivclassName(target));
            //Can we kick this user by PC order?
            //If GPC is GT, Yes.
            if (int.Parse(sender.CurrentUser.getInfo("gpc_id")) > int.Parse(target.CurrentUser.getInfo("gpc_id")))
            {
                return true;
            }
            if (int.Parse(sender.CurrentUser.getInfo("gpc_id")) < int.Parse(target.CurrentUser.getInfo("gpc_id")))
            {
                return false;
            }
            if (TPI.Order > SPI.Order)
            {
                return false;
            }
            //Kick the user.
            return true;
        }
        [PacketHandler("send", "admin")]
        protected void OnAdminCommand(Packet p, IConnection sender)
        {
            //ah fuck.
            //I really didn't want to implement this. Its such a pain in the ass.
            string[] cmdTokens = p.SubPacket.body.Split(' ');
            //0 - command, 1 - mainParam, 2+ - additional params.
            if (cmdTokens.Length < 2)
            {
                SendErrorPacketToClient(sender, p, "bad command");
                return;
            }
            try
            {
                switch (cmdTokens[0].ToLower())
                {
                    case "rename":
                        if (cmdTokens[1].ToLower() != "privclass")
                        {
                            SendErrorPacketToClient(sender, p, "bad command");
                            return;
                        }
                        HandleRenameCommand(sender, p, cmdTokens);
                        break;
                    case "create":
                        if (cmdTokens[1].ToLower() != "privclass")
                        {
                            SendErrorPacketToClient(sender, p, "bad command");
                            return;
                        }
                        HandleCreateCommand(sender, p, cmdTokens);
                        break;
                    case "update":
                        if (cmdTokens[1].ToLower() != "privclass")
                        {
                            SendErrorPacketToClient(sender, p, "bad command");
                            return;
                        }
                        HandleUpdateCommand(sender, p, cmdTokens);
                        break;
                    case "remove":
                        if (cmdTokens[1].ToLower() != "privclass")
                        {
                            SendErrorPacketToClient(sender, p, "bad command");
                            return;
                        }
                        HandleRemoveCommand(sender, p, cmdTokens);
                        break;
                    case "move":
                        //move users Test1 to Test2
                        if (cmdTokens.Length != 5 || cmdTokens[1] != "users" || cmdTokens[3] != "to")
                        {
                            SendErrorPacketToClient(sender, p, "bad command");
                            return;
                        }
                        HandleMoveCommand(sender, p, cmdTokens);
                        break;
                    case "show":
                    case "showverbose":
                        switch (cmdTokens[1].ToLower())
                        {
                            case "privclass":
                                HandleShowPrivclasses(sender, p, cmdTokens);
                                break;
                            case "users":
                                HandleShowUsers(sender, p, cmdTokens);
                                break;
                            default:
                                SendErrorPacketToClient(sender, p, "bad command");
                                return;
                        }
                        break;
                    default:
                        break;
                }
            }
            catch (NotImplementedException)
            {
                SendErrorPacketToClient(sender, p, "method not implemented");
            }
        }
        const string MoveMembersSQL = "UPDATE `tbl_privclass_members` SET `id`=@newid WHERE `id`=@oldid;";
        private void HandleMoveCommand(IConnection sender, Packet p, string[] cmdTokens)
        {
            if (!Demand(sender, new PermissionRule("admin", 0, Comparison.NEQ)))
            {
                SendErrorPacketToClient(sender, p, "not privileged");
                return;
            }
            //This is easy as pie.
            PrivclassInfo src = mMyManager.GetPrivclassFromName(cmdTokens[2]);
            PrivclassInfo dst = mMyManager.GetPrivclassFromName(cmdTokens[4]);
            if (src == null)
            {
                SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[2]);
                return;
            }
            if (dst == null)
            {
                SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[4]);
                return;
            }
            //Both src and dst are good, and user has priv's, Lets do this, I'm a cashew.
            int n = SDBackend.ExecuteNonQuery(MoveMembersSQL, new MySqlParameter("@newid", dst.Id), new MySqlParameter("@oldid", src.Id));
            //And build our packet
            Packet outPkt = Packet.EmptyPacket;
            outPkt.cmd = "admin";
            outPkt.param = "move";
            outPkt.args["p"] = "privclass";
            outPkt.args["by"] = sender.CurrentUser.getInfo("username");
            outPkt.args["prev"] = src.Name;
            outPkt.args["name"] = dst.Name;
            outPkt.args["n"] = n.ToString();
            outPkt.body = outPkt;
            outPkt.args.Clear();
            outPkt.cmd = "recv";
            outPkt.param = this.Namespace;
            SendToAll(outPkt.ToString());
            PCUpdated(sender); //We updated members, but we should just resend everything for the hell of it.
        }
        string pcRegex = @"^[A-Za-z0-9\-_]+$"; //No spaces.
        const string SQLRenamePrivclass = @"UPDATE `tbl_privclass` SET `name`=@newname WHERE `id`=@pcid;";
        private PermissionRuleSet BuildRuleSetFromString(string[] tokens, int startIndex, int count)
        {
            PermissionRuleSet ret = new PermissionRuleSet();
            for (int i = startIndex; i < (startIndex + count); i++)
            {
                //Its either + or - rule or rule=val
                string name = string.Empty;
                int val = 0;
                if (tokens[i].StartsWith("+"))
                {
                    name = tokens[i].Substring(1);
                    val = 1;
                }
                else if (tokens[i].StartsWith("-"))
                {
                    name = tokens[i].Substring(1);
                    val = 0;
                }
                else
                {
                    string[] subTokens = tokens[i].Split('=');
                    if (subTokens.Length != 2)
                        throw new InvalidOperationException("Not a valid token " + tokens[i]);
                    //ok it seems valid, now get the value, clamp at 0-99
                    if (!int.TryParse(subTokens[1], out val))
                        throw new InvalidOperationException("Not a valid token " + tokens[i]);
                    if (val < 0)
                        val = 0;
                    if (val > 99)
                        val = 99;
                    if (subTokens[0].Length == 0)
                        throw new InvalidOperationException("Not a valid token " + tokens[i]);
                    name = subTokens[0];
                }
                ret[name] = new PermissionRule(name.ToLower(), val, Comparison.NOP);
            }
            return ret;
        }
        private void HandleRenameCommand(IConnection sender, Packet p, string[] cmdTokens)
        {
            if (!this.Demand(sender, new PermissionRule("admin", 0, Comparison.NEQ)))
            {
                SendErrorPacketToClient(sender, p, "not privileged");
                return;
            }
            //Its always privclass
            //There will also always be 5 tokens, if there are not, then the rename is not valid.
            if (cmdTokens.Length != 5)
            {
                SendErrorPacketToClient(sender, p, "bad command");
                return;
            }
            PrivclassInfo targetPc = mMyManager.GetPrivclassFromName(cmdTokens[2]);
            string newName = cmdTokens[4];
            if (targetPc == null)
            {
                SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[2]);
                return;
            }
            if (!Regex.IsMatch(newName, pcRegex, RegexOptions.Compiled | RegexOptions.IgnoreCase))
            {
                SendErrorPacketToClient(sender, p, "bad privclass name: " + newName);
                return;
            }
            //We are all set.
            SDBackend.ExecuteNonQuery(SQLRenamePrivclass, new MySqlParameter("@newname", newName), new MySqlParameter("@pcid", targetPc.Id));
            Packet outPacket = Packet.EmptyPacket;
            outPacket.cmd = "admin";
            outPacket.param = "rename";
            outPacket.args["p"] = "privclass";
            outPacket.args["by"] = sender.CurrentUser.getInfo("username");
            outPacket.args["prev"] = targetPc.Name;
            //Now we refresh
            targetPc.Refresh();
            outPacket.args["name"] = targetPc.Name;
            //Thats it for this one.
            outPacket.body = outPacket;
            outPacket.args.Clear();
            outPacket.cmd = "recv";
            outPacket.param = this.Namespace;
            SendToAll(outPacket.ToString());
            PCUpdated(sender);
        }
        private string FormatPermission(PermissionRule p)
        {
            if (p.Value <= 1 && !(p.Name == "demote" && p.Value == 1))
                return string.Format("{0}{1}", p.Value == 0 ? "-" : "+", p.Name);
            else
                return string.Format("{0}={1}", p.Name, p.Value);
        }
        private void HandleShowPrivclasses(IConnection sender, Packet p, string[] cmdTokens)
        {
            bool verbose = cmdTokens[0].ToLower() == "showverbose";
            PermissionRuleSet DefaultPermissions = mMyManager.GetPermissions(-1); //-1 is default privs for chat:, -2 is default for pchat:
            List<PrivclassInfo> pcs = new List<PrivclassInfo>();
            if (cmdTokens.Length == 2)
            {
                //Show all
                pcs.AddRange(mMyManager.GetAllPrivclasses());
            }
            else
            {
                for (int x = 2; x < cmdTokens.Length; x++)
                {
                    PrivclassInfo pc = mMyManager.GetPrivclassFromName(cmdTokens[x]);
                    if (pc == null)
                    {
                        SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[x]);
                        return;
                    }
                    if (!pcs.Contains(pc))
                        pcs.Add(pc);
                }
                //Show some.
            }
            //Finally
            if (pcs.Count == 0)
            {
                SendErrorPacketToClient(sender, p, "no privclasses to display?");
            }
            StringBuilder body = new StringBuilder();
            for (int i = 0; i < pcs.Count; i++)
            {
                PermissionRuleSet pr = mMyManager.GetPermissions(pcs[i]);
                body.AppendFormat("{0} order={1}", pcs[i].Name, pcs[i].Order);

                foreach (PermissionRule r in pr)
                {
                    if (DefaultPermissions.Demand(new PermissionRule(r.Name, r.Value, Comparison.NEQ)) || verbose)
                    {
                        body.Append(" ");
                        //Its not default, or we are being verbose.
                        body.Append(FormatPermission(r));
                    }
                }
                body.Append("\n");
            }
            //Ok, we now have our body.
            //Build the final packet.
            Packet outPacket = Packet.EmptyPacket;
            outPacket.cmd = "admin";
            outPacket.param = "show";
            outPacket.args["p"] = "privclass";
            outPacket.body = body.ToString();
            outPacket.body = outPacket;
            outPacket.args.Clear();
            outPacket.cmd = "recv";
            outPacket.param = this.Namespace;
            sender.Send(outPacket);
        }

        private void HandleShowUsers(IConnection sender, Packet p, string[] cmdTokens)
        {
            List<PrivclassInfo> pcs = new List<PrivclassInfo>();
            if (cmdTokens.Length == 2)
            {
                //Show all
                pcs.AddRange(mMyManager.GetAllPrivclasses());
            }
            else
            {
                for (int x = 2; x < cmdTokens.Length; x++)
                {
                    PrivclassInfo pc = mMyManager.GetPrivclassFromName(cmdTokens[x]);
                    if (pc == null)
                    {
                        SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[x]);
                        return;
                    }
                    if (!pcs.Contains(pc))
                        pcs.Add(pc);
                }
                //Show some.
            }
            //ok
            if (pcs.Count == 0)
            {
                SendErrorPacketToClient(sender, p, "no privclasses to display?");
            }
            StringBuilder body = new StringBuilder();
            for (int i = 0; i < pcs.Count; i++)
            {
                IUserData[] pcmembers = pcs[i].GetAllMembers();
                body.AppendFormat("{0}:", pcs[i].Name);
                for (int j = 0; j < pcmembers.Length; j++)
                {
                    body.AppendFormat(" {0}{1}", pcmembers[j].getInfo("symbol"), pcmembers[j].getInfo("username"));
                }
                body.Append("\n");
            }
            //Ok, we now have our body.
            //Build the final packet.
            Packet outPacket = Packet.EmptyPacket;
            outPacket.cmd = "admin";
            outPacket.param = "show";
            outPacket.args["p"] = "users";
            outPacket.body = body.ToString();
            outPacket.body = outPacket;
            outPacket.args.Clear();
            outPacket.cmd = "recv";
            outPacket.param = this.Namespace;
            sender.Send(outPacket);

        }
        private void HandleCreateCommand(IConnection sender, Packet p, string[] cmdTokens)
        {
            if (!this.Demand(sender, new PermissionRule("admin", 0, Comparison.NEQ)))
            {
                SendErrorPacketToClient(sender, p, "not privileged");
                return;
            }
            if (cmdTokens.Length < 3)
            {
                SendErrorPacketToClient(sender, p, "bad command");
                return;
            }
            string newName = cmdTokens[2];
            int order = 0;
            PermissionRuleSet newPermissions = new PermissionRuleSet();
            if (cmdTokens.Length > 3)
            {
                try
                {
                    newPermissions = BuildRuleSetFromString(cmdTokens, 3, cmdTokens.Length - 3);
                }
                catch (InvalidOperationException)
                {
                    SendErrorPacketToClient(sender, p, "invalid permission string " + string.Join(" ", cmdTokens, 3, cmdTokens.Length - 3));
                    return;
                }
            }
            List<int> ordersTaken = new List<int>();
            List<string> takenNames = new List<string>();
            foreach (PrivclassInfo pi in mMyManager.GetPrivclasses())
            {
                ordersTaken.Add(pi.Order);
                takenNames.Add(pi.Name.ToLower()); //We do tolower, to prevent having Founders and founders as privclasses.
            }
            if (newPermissions.HasRule(new PermissionRule("order", 0, Comparison.NOP)))
            {
                order = newPermissions["order"].Value;
                if (order < 0 || order > 99 || ordersTaken.Contains(order))
                {
                    SendErrorPacketToClient(sender, p, "invalid order");
                    return;
                }
            }
            else
            {
                //Search for a free order.
                //First build a list of orders:
                for (int x = 1; x <= 99; x++)
                {
                    if (!ordersTaken.Contains(x))
                    {
                        order = x;
                        break;
                    }
                }
                if (order == 0)
                {
                    SendErrorPacketToClient(sender, p, "no free privclass orders");
                    return;
                }
            }
            //Ok order is good when we get here.
            //Now we have to check the name
            
            //We can't just look up the PC because its case sensitive,
            //So do it the same way we checked for orders.

            if (takenNames.Contains(newName.ToLower()))
            {
                SendErrorPacketToClient(sender, p, "privclass exists: " + newName);
                return;
            }
            {
                PermissionRuleSet correctedPrivs = new PermissionRuleSet();
                for (int x = 0; x < validPrivs.Length; x++)
                {
                    if (newPermissions.HasRule(new PermissionRule(validPrivs[x], 0)))
                    {
                        correctedPrivs[validPrivs[x]] = newPermissions[validPrivs[x]];
                    }
                }
                PermissionRuleSet defaultPermissions = mMyManager.GetPermissions(-1);
                //This gives us a compressed rule set, so we can drop all permissions, and then re-insert these ones.
                correctedPrivs.RemoveRuleSet(defaultPermissions, true);
                newPermissions = correctedPrivs;
            }
            //Now create the entry in tbl_privclass
            SDBackend.ExecuteNonQuery(CreatePrivclassSql, new MySqlParameter("@name", newName), new MySqlParameter("@cid", this.ID), new MySqlParameter("@order", order));
            mMyManager.ReLoadPCs(); //Force the privclasses to reload
            PrivclassInfo target = mMyManager.GetPrivclassFromName(newName); //Grab a copy.
            //Next we insert permissions:
            using (MySqlConnection conn = SDBackend.CreateConnection())
            {
                foreach (PermissionRule newp in newPermissions)
                {
                    SDBackend.InsertPermission(conn, newp.Name, newp.Value, target.Id);
                }
            }
            //Drop the permission rule set please.
            mMyManager.DropPermissionsFromCache(target.Id);
            Packet outPkt = Packet.EmptyPacket;
            outPkt.cmd = "admin";
            outPkt.param = "create";
            outPkt.args["p"] = "privclass";
            outPkt.args["by"] = sender.CurrentUser.getInfo("username");
            outPkt.args["name"] = target.Name;
            StringBuilder privString = new StringBuilder();
            if (order != 0)
                privString.AppendFormat("order={0} ", order);
            foreach (PermissionRule r in newPermissions)
            {
                privString.Append(FormatPermission(r));
                privString.Append(" ");
            }
            outPkt.args["privs"] = privString.ToString().Trim();
            outPkt.DumpToBody();
            outPkt.cmd = "recv";
            outPkt.param = this.Namespace;
            SendToAll(outPkt.ToString());
            PCUpdated(sender);

        }
        const string CreatePrivclassSql = "INSERT INTO tbl_privclass (`name`, `channel`, `order`) VALUES ( @name, @cid, @order);";
        static string[] validPrivs = { "kick", "promote", "demote", "topic", "title", "botdirective", "motd", "admin", "msg", "join", "shownotice" };
        const string DropAllPrivs = "DELETE FROM tbl_privclass_permissions WHERE `privclass_id`=@pcid;";
        const string ChangeOrderSQL = "UPDATE `tbl_privclass` SET `order`=@order WHERE `id`=@pcid;";
        private void HandleUpdateCommand(IConnection sender, Packet p, string[] cmdTokens)
        {
            //update privclass name var1=val1 etc
            int order = 0;
            PermissionRuleSet newPrivs = new PermissionRuleSet();
            PrivclassInfo target = mMyManager.GetPrivclassFromName(cmdTokens[2]);
            if (!this.Demand(sender, new PermissionRule("admin", 0, Comparison.NEQ)))
            {
                SendErrorPacketToClient(sender, p, "not privileged");
                return;
            }
            if (target == null)
            {
                SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[2]);
                return;
            }
            if (cmdTokens.Length > 3)
            {
                try
                {
                    newPrivs = BuildRuleSetFromString(cmdTokens, 3, cmdTokens.Length - 3);
                }
                catch (InvalidOperationException)
                {
                    SendErrorPacketToClient(sender, p, "invalid permission string " + string.Join(" ", cmdTokens, 3, cmdTokens.Length - 3));
                    return;
                }
            }
            if (newPrivs.HasRule(new PermissionRule("order", 0)) && newPrivs["order"].Value != 0)
            {
                order = newPrivs["order"].Value;
            }
            PermissionRuleSet DisplayPrivs = new PermissionRuleSet();
            {
                PermissionRuleSet correctedPrivs = new PermissionRuleSet();
                for (int x = 0; x < validPrivs.Length; x++)
                {
                    if (newPrivs.HasRule(new PermissionRule(validPrivs[x], 0)))
                    {
                        DisplayPrivs[validPrivs[x]] = correctedPrivs[validPrivs[x]] = newPrivs[validPrivs[x]];
                    }
                    else
                    {
                        correctedPrivs[validPrivs[x]] = mMyManager.GetPermissions(target.Id)[validPrivs[x]];
                    }
                }
                PermissionRuleSet defaultPermissions = mMyManager.GetPermissions(-1);
                //This gives us a compressed rule set, so we can drop all permissions, and then re-insert these ones.
                correctedPrivs.RemoveRuleSet(defaultPermissions, true);
                DisplayPrivs.RemoveRuleSet(defaultPermissions, true);
                newPrivs = correctedPrivs;
            }
            //Finally
            if (order != 0)
            {
                foreach (PrivclassInfo pi in mMyManager.GetPrivclasses())
                {
                    if (pi.Order == order)
                    {
                        SendErrorPacketToClient(sender, p, "a privclass already exists with that order");
                        return;
                    }
                }
                //If we get here, Its time to update the order.
                SDBackend.ExecuteNonQuery(ChangeOrderSQL, new MySqlParameter("@order", order), new MySqlParameter("@pcid", target.Id));

            }
            SDBackend.ExecuteNonQuery(DropAllPrivs, new MySqlParameter("@pcid", target.Id));
            using (MySqlConnection conn = SDBackend.CreateConnection())
            {
                foreach (PermissionRule newp in newPrivs)
                {
                    if(newp != null)
                    SDBackend.InsertPermission(conn, newp.Name, newp.Value, target.Id);
                }
            }
            mMyManager.DropPermissionsFromCache(target.Id);
            //finally.
            Packet outPkt = Packet.EmptyPacket;
            outPkt.cmd = "admin";
            outPkt.param = "update";
            outPkt.args["p"] = "privclass";
            outPkt.args["by"] = sender.CurrentUser.getInfo("username");
            outPkt.args["name"] = target.Name;
            StringBuilder privString = new StringBuilder();
            if (order != 0)
                privString.AppendFormat("order={0} ", order);
            foreach (PermissionRule r in DisplayPrivs)
            {
                privString.Append(FormatPermission(r));
                privString.Append(" ");
            }
            outPkt.args["privs"] = privString.ToString().Trim();
            outPkt.body = outPkt;
            outPkt.args.Clear();
            outPkt.cmd = "recv";
            outPkt.param = this.Namespace;
            SendToAll(outPkt.ToString());
            if (order != 0)
            {
                PCUpdated(sender);
            }
            else
            {
                //Just recheck the bans, we only reloaded the permissions.
                //IE No need to send privclass and members packet.
                RecheckBans(sender);
            }
        }
        //const string MoveMembersSQL = "UPDATE `tbl_privclass_members` SET `id`=@newid WHERE `id`=@oldid;";
        const string RemoveMembersSQL = "DELETE FROM `tbl_privclass_members` WHERE `id`=@pcid;";
        //Remove the users seperately, so we know how many were affected.
        const string RemovePrivclassSQL = "DELETE FROM `tbl_privclass` WHERE `id`=@pcid; DELETE FROM `tbl_privclass_permissions` WHERE `privclass_id`=@pcid;";
        private void HandleRemoveCommand(IConnection sender, Packet p, string[] cmdTokens)
        {
            if (cmdTokens.Length != 3)
            {
                SendErrorPacketToClient(sender, p, "bad command");
            }
            PrivclassInfo target = mMyManager.GetPrivclassFromName(cmdTokens[2]);
            if (!this.Demand(sender, new PermissionRule("admin", 0, Comparison.NEQ)))
            {
                SendErrorPacketToClient(sender, p, "not privileged");
                return;
            }
            if (target == null)
            {
                SendErrorPacketToClient(sender, p, "no such privclass: " + cmdTokens[2]);
                return;
            }
            MySqlParameter pcid = new MySqlParameter("@pcid", target.Id);
            SDBackend.ExecuteNonQuery(RemovePrivclassSQL, pcid);
            int a = SDBackend.ExecuteNonQuery(RemoveMembersSQL, pcid);
            Packet outPkt = Packet.EmptyPacket;
            outPkt.cmd = "admin";
            outPkt.param = "remove";
            outPkt.args["p"] = "privclass";
            outPkt.args["by"] = sender.CurrentUser.getInfo("username");
            outPkt.args["name"] = target.Name;
            outPkt.args["n"] = a.ToString();
            outPkt.body = outPkt;
            outPkt.args.Clear();
            outPkt.cmd = "recv";
            outPkt.param = this.Namespace;
            SendToAll(outPkt.ToString());
            PCUpdated(sender);
        }
        private void PCUpdated(IConnection sender)
        {
            mMyManager.ReLoadPCs();
            SendToAll(mMyManager.GetPrivclassPacket().ToString());
            SendToAll(mMyManager.getMembersPacket().ToString());
            RecheckBans(sender);
        }
        private void RecheckBans(IConnection conn)
        {
            IConnection[] conns = GetAllConnectionsPresent();
            List<IConnection> selected = new List<IConnection>();
            foreach (IConnection c in conns)
            {
                if (!Demand(c, new PermissionRule("join", 0, Comparison.NEQ)))
                {
                    KickUser(c.CurrentUser, conn.CurrentUser, "not privileged");
                }
            }
        }
        [PacketHandler("send", "ban")]
        [PacketHandler("send", "unban")]
        [PacketHandler("send", "demote")]
        [PacketHandler("send", "promote")]
        protected void OnPromotePacket(Packet p, IConnection conn)
        {
            Packet sub = p.SubPacket;
            PrivclassInfo target = null;
            IUserData targetUser = GlobalUserCache.GetUser(sub.param);
            if (targetUser == null)
            {
                SendErrorPacketToClient(conn, p, "no such user");
                return;
            }
            switch (sub.cmd)
            {
                case "ban":
                    //Get the target privclass;
                    target = mMyManager.FindBannedPrivclass();
                    break;
                case "unban":
                    target = mMyManager.GetGuestPrivclass();
                    break;
                case "promote":
                    if (string.IsNullOrEmpty(sub.body.Trim()))
                        target = mMyManager.GetNextPrivclass(mMyManager.GetUserPrivclass(targetUser));
                    else
                        target = mMyManager.GetPrivclassFromName(sub.body.Trim());
                    break;
                case "demote":
                    if (string.IsNullOrEmpty(sub.body.Trim()))
                        target = mMyManager.GetPreviousPrivclass(mMyManager.GetUserPrivclass(targetUser));
                    else
                        target = mMyManager.GetPrivclassFromName(sub.body.Trim());
                    break;
            }
            if (target == null)
            {
                SendErrorPacketToClient(conn, p, "no such privclass");
                return;
            }
            if (!Demand(conn, new PermissionRule("promote", target.Order, Comparison.GTEQ)) ||
                !Demand(conn, new PermissionRule("demote", target.Order, Comparison.LTEQ)))
            {
                SendErrorPacketToClient(conn, p, "not privileged");
                return;
            }
            //Ok, Lets do this
            if (!mMyManager.SetUserPrivclass(targetUser, target))
            {
                //This only returns false when the update would result in no change to the data.
                //So we can just return here, Don't say a word to clients.
                return;
            }
            //Send the privchg packet.
            Packet outPkt = Packet.EmptyPacket;
            outPkt.cmd = "privchg";
            outPkt.param = targetUser.getInfo("username");
            outPkt.args["by"] = conn.CurrentUser.getInfo("username");
            outPkt.args["pc"] = target.Name;
            if (mMyManager.Demand(targetUser, new PermissionRule("shownotice", 0, Comparison.NEQ)))
                outPkt.args["i"] = "1";
            else
                outPkt.args["i"] = "0";
            outPkt.body = outPkt;
            outPkt.args.Clear();
            outPkt.cmd = "recv";
            outPkt.param = this.Namespace;
            SendToAll(outPkt);
            RecheckBans(conn);
        }
    }
}
